#pragma once
#include <iostream>
#include <filesystem>
#include <unordered_map>
#include <optional>
#include <fstream>
#include <vector>
#include "MFSI.hpp"

// QU_MEMORY_FILE_SYSTEM BY ZERO123
namespace QMFS
{
	// 标准化path路径
	inline std::filesystem::path pathLexicallyNormal(const std::filesystem::path& path)
	{
		return path.lexically_normal();
	}

	// 标准化path字符串路径
	inline std::string pathLexicallyNormal(const std::string& path, bool isChildPath=false)
	{
		std::string newPath = pathLexicallyNormal(std::filesystem::path(path)).string();
		std::replace(newPath.begin(), newPath.end(), '\\', '/');
		if (isChildPath && newPath.size() > 0)
		{
			if (newPath.back() == '/')
			{
				// 删除末结构符
				newPath.pop_back();
			}
			if (newPath.size() > 0 && newPath[0] == '/')
			{
				// 删除首结构符
				newPath.erase(0, 1);
			}
		}
		return newPath;
	}

	class Path
	{
	private:
		Path* parent = nullptr;
		std::unordered_map<std::string, Path> childs;
		std::string pathName;
		std::optional<std::vector<char>> fileBytes = std::nullopt;
		bool _isDir = false;
	public:
		Path(const std::string& pathName="", Path* parent = nullptr) : pathName(pathName), parent(parent) {}

		// 基于name查询子节点(如果存在) 支持多级目录
		std::optional<Path*> getChild(const std::string& path)
		{
			std::string buf;
			std::string normalPath = pathLexicallyNormal(path, true);
			size_t start = 0;
			size_t end = normalPath.find("/");
			std::unordered_map<std::string, Path>* targetMap = &this->childs;
			while (end != std::string::npos)
			{
				buf = normalPath.substr(start, end - start);
				start = end + 1;
				end = normalPath.find("/", start);
				if (targetMap->find(buf) == targetMap->end())
				{
					// 找不到目录 抛出空值
					return std::nullopt;
				}
				else
				{
					targetMap = &(*targetMap)[buf].childs;
				}
			}
			// LAST VALUE
			buf = normalPath.substr(start);
			if (targetMap->find(buf) != targetMap->end())
			{
				return &(*targetMap)[buf];
			}
			return std::nullopt;
		}

		bool hasChild(const std::string& path)
		{
			if (getChild(path))
			{
				return true;
			}
			return false;
		}

		// 创建Path 如果存在将会自动跳过 并返回最后一个Path的引用
		Path& createPath(const std::string& path, bool mustIsDir=false)
		{
			std::string buf;
			std::string normalPath = pathLexicallyNormal(path, true);
			size_t start = 0;
			size_t end = normalPath.find("/");
			Path* parent = this;
			while (end != std::string::npos)
			{
				buf = normalPath.substr(start, end - start);
				start = end + 1;
				end = normalPath.find("/", start);
				if (parent->childs.find(buf) == parent->childs.end())
				{
					// 不存在目录 创建目录
					parent->childs[buf] = std::move(Path(buf, parent));
				}
				parent = &parent->childs[buf];
			}
			// LAST VALUE
			buf = normalPath.substr(start);
			if (parent->childs.find(buf) == parent->childs.end())
			{
				parent->childs[buf] = std::move(Path(buf, parent));
			}
			auto& outPath = parent->childs[buf];
			if (mustIsDir)
			{
				outPath.setIsDir(true);
			}
			return outPath;
		}

		// 获取Path列表
		std::vector<Path*> getChilds()
		{
			std::vector<Path*> out;
			for (auto& data : childs)
			{
				out.push_back(&data.second);
			}
			return out;
		}

		bool empty() const
		{
			return !isFileType() && !isDirType();
		}

		static void _getAllFiles(Path* path, std::vector<Path*>* out)
		{
			for (auto node : path->getChilds())
			{
				if (node->isFileType())
				{
					out->push_back(node);
				}
				if (node->isDirType())
				{
					_getAllFiles(node, out);
				}
			}
		}

		// 获取当前Path所有的文件(所有层级)
		std::vector<Path*> getAllFiles()
		{
			std::vector<Path*> ptrVc;
			_getAllFiles(this, &ptrVc);
			return ptrVc;
		}

		// 检查当前Path是不是文件类型(也可能和文件夹同时存在)
		bool isFileType() const
		{
			if (fileBytes)
			{
				return true;
			}
			return false;
		}

		// 检查当前Path是不是目录类型(也可能和同名文件一起存在)
		bool isDirType() const
		{
			if (childs.size() > 0)
			{
				return true;
			}
			return _isDir;
		}

		// 写入字节到当前文件(若非文件将自动创建)
		void writeFile(const std::vector<char>& bytes)
		{
			fileBytes = bytes;
		}

		void writeFile(const std::vector<char>&& bytes)
		{
			fileBytes = bytes;
		}

		void writeFile(const std::string& str)
		{
			std::vector<char> vec;
			for (char c : str)
			{
				vec.push_back(c);
			}
			writeFile(vec);
		}

		// 字符串追加模式写入文件(请确保是文本类型文件)
		void addModeWriteFile(const std::string& str)
		{
			if (!fileBytes)
			{
				fileBytes = std::vector<char>();
			}
			for (char c : str)
			{
				(*fileBytes).push_back(c);
			};
		}

		void copyPhyFile(const std::filesystem::path& path)
		{
			// 是文件
			std::ifstream file(path, std::ios::binary | std::ios::ate);
			if (!file.is_open())
			{
				return;
			}
			// 获取文件大小
			std::streamsize fileSize = file.tellg();
			file.seekg(0, std::ios::beg);
			std::vector<char> fileContent(fileSize);
			// 读取文件内容到vector中
			if (!file.read(fileContent.data(), fileSize))
			{
				return;
			}
			writeFile(fileContent);
		}

		// 读取当前文件Bytes 可能为空 请检查类型
		std::optional<std::vector<char>>& readFile()
		{
			return fileBytes;
		}

		// 读取并解析为文本内容(请确保该为有效的文本内容)
		std::string readTxt()
		{
			auto& bytes = readFile();
			if (!bytes)
			{
				return "";
			}
			return std::string(bytes->data(), bytes->size());
		}

		// 删除文件数据
		void removeFile()
		{
			if (fileBytes)
			{
				fileBytes = std::nullopt;
			}
		}

		// 删除所有子目录 包括文件
		void removeChildDirs()
		{
			childs.clear();
		}

		// 从Parent中删除自己的所有数据(包括文件和文件夹)
		void removeSelfAll()
		{
			if (parent != nullptr && parent->childs.find(pathName) != parent->childs.end())
			{
				if (&(parent->childs[pathName]) == this)
				{
					parent->childs.erase(pathName);
				}
			}
		}

		// 删除SELF子目录(从parent中试图删除自己)
		void removeSelfDir()
		{
			if (!isDirType())
			{
				return;
			}
			removeSelfAll();
		}

		// 显性声明是文件夹 (在非显性声明时 若不包含任何子节点将不会构成文件夹)
		void setIsDir(bool state)
		{
			_isDir = state;
		}

		// 获取Parent目录 如果不存在Parent则返回自己
		Path& getParent()
		{
			if (parent == nullptr)
			{
				return *this;
			}
			return *parent;
		}
		
		std::string getName() const
		{
			return pathName;
		}

		const std::string& getNameRef() const
		{
			return pathName;
		}

		// 获取完整目录
		std::string getPath() const
		{
			if (parent == nullptr)
			{
				return pathName;
			}
			return parent->getPath() + "/" + pathName;
		}

		// >>>>>>>>> C STRUCT兼容 <<<<<<<<<
		static const char* MFSI_CALL C_GetPath(void* ptr, char* out)
		{
			QMFS::Path* obj = static_cast<QMFS::Path*>(ptr);
			std::string str = obj->getPath();
			std::copy(str.c_str(), str.c_str() + str.size() + 1, out);
			return out;
		}

		static IMemoryPath MFSI_CALL C_GetParent(void* ptr)
		{
			IMemoryPath parentStruct{};
			QMFS::Path* obj = static_cast<QMFS::Path*>(ptr);
			obj->getParent().tranToCStruct(&parentStruct);
			return parentStruct;
		}

		static bool MFSI_CALL C_IsDir(void* ptr)
		{
			QMFS::Path* obj = static_cast<QMFS::Path*>(ptr);
			return obj->isDirType();
		}

		static bool MFSI_CALL C_IsFile(void* ptr)
		{
			QMFS::Path* obj = static_cast<QMFS::Path*>(ptr);
			return obj->isFileType();
		}

		static IMemoryBytes MFSI_CALL C_ReadByte(void* ptr)
		{
			QMFS::Path* obj = static_cast<QMFS::Path*>(ptr);
			auto& data = obj->readFile();
			if (data)
			{
				return IMemoryBytes{ (ByteSize)data->size(), data->data() };
			}
			return IMemoryBytes{ 0, NULL };
		}

		static bool MFSI_CALL C_WriteByte(void* ptr, const IMemoryBytes* bytes)
		{
			QMFS::Path* obj = static_cast<QMFS::Path*>(ptr);
			obj->writeFile(std::vector<char>(bytes->bytes, bytes->bytes + bytes->size));
			return true;
		}

		static bool MFSI_CALL C_HasChild(void* ptr, const char* pathName)
		{
			QMFS::Path* obj = static_cast<QMFS::Path*>(ptr);
			return obj->hasChild(pathName);
		}

		static IMemoryPath MFSI_CALL C_GetChild(void* ptr, const char* pathName)
		{
			QMFS::Path* obj = static_cast<QMFS::Path*>(ptr);
			auto child = obj->getChild(pathName);
			if (child)
			{
				return (*child)->getCStruct();
			}
			return IMemoryPath{ NULL };
		}

		static void MFSI_CALL C_GetChilds(void* ptr, void* contextPtr, IMemoryPathWriteCallBack func)
		{
			QMFS::Path* obj = static_cast<QMFS::Path*>(ptr);
			for (auto pathPtr : obj->getChilds())
			{
				auto cData = pathPtr->getCStruct();
				func(contextPtr, &cData);
			}
		}

		static void MFSI_CALL C_SetDirType(void* ptr, bool state)
		{
			static_cast<QMFS::Path*>(ptr)->setIsDir(state);
		}

		static IMemoryPath MFSI_CALL C_CreatePath(void* ptr, const char* pathName)
		{
			return static_cast<QMFS::Path*>(ptr)->createPath(pathName).getCStruct();
		}

		static bool MFSI_CALL C_RemoveFile(void* ptr)
		{
			static_cast<QMFS::Path*>(ptr)->removeFile();
			return true;
		}

		static bool MFSI_CALL C_RemoveChilds(void* ptr)
		{
			static_cast<QMFS::Path*>(ptr)->removeChildDirs();
			return true;
		}

		static bool MFSI_CALL C_RemoveSelf(void* ptr)
		{
			static_cast<QMFS::Path*>(ptr)->removeSelfAll();
			return true;
		}

		static const char* MFSI_CALL C_GetFileName(void* ptr)
		{
			return static_cast<QMFS::Path*>(ptr)->getNameRef().c_str();
		}

		static void MFSI_CALL C_GetAllFiles(void* ptr, void* contextPtr, IMemoryPathWriteCallBack func)
		{
			QMFS::Path* obj = static_cast<QMFS::Path*>(ptr);
			for (auto pathPtr : obj->getAllFiles())
			{
				auto cData = pathPtr->getCStruct();
				func(contextPtr, &cData);
			}
		}

		static bool MFSI_CALL C_AddModeWriteFile(void* ptr, const char* str)
		{
			static_cast<QMFS::Path*>(ptr)->addModeWriteFile(str);
			return true;
		}

		void tranToCStruct(IMemoryPath* ptr) const
		{
			ptr->_this = const_cast<void*>(static_cast<const void*>(this));
			ptr->getPath = C_GetPath;
			ptr->parent = C_GetParent;
			ptr->isDir = C_IsDir;
			ptr->isFile = C_IsFile;
			ptr->writeBytes = C_WriteByte;
			ptr->readBytes = C_ReadByte;
			ptr->hasChild = C_HasChild;
			ptr->getChild = C_GetChild;
			ptr->getChilds = C_GetChilds;
			ptr->dirDec = C_SetDirType;
			ptr->createPath = C_CreatePath;
			ptr->removeFile = C_RemoveFile;
			ptr->removeChilds = C_RemoveChilds;
			ptr->removeSelf = C_RemoveSelf;
			ptr->getFileName = C_GetFileName;
			ptr->getAllFiles = C_GetAllFiles;
			ptr->addModeWriteFile = C_AddModeWriteFile;
		}

		IMemoryPath getCStruct() const
		{
			IMemoryPath str{};
			tranToCStruct(&str);
			return str;
		}

		// 读取物理文件数据
		static std::vector<char> readPhyFile(const std::filesystem::path& phyPath)
		{
			std::ifstream file(phyPath, std::ios::binary | std::ios::ate);
			if (!file.is_open())
			{
				return {};
			}
			// 获取文件大小
			std::streamsize fileSize = file.tellg();
			file.seekg(0, std::ios::beg);
			std::vector<char> fileContent(fileSize);
			// 读取文件内容到vector中
			file.read(fileContent.data(), fileSize);
			return fileContent;
		}

		// 加载内存目录到物理路径
		void createDatasToPhyPath(const std::filesystem::path& outPath, Path* ptr = nullptr, const std::vector<std::string>& blackExtension = {}, bool hotWrite=false)
		{
			if (ptr == nullptr)
			{
				ptr = this;
				std::filesystem::create_directories(outPath);
			}
			for (auto data : ptr->getChilds())
			{
				auto phyPath = outPath / data->getName();
				if (data->isFileType() && std::find(blackExtension.begin(), blackExtension.end(), phyPath.extension()) == blackExtension.end())
				{
					const auto& bytesArray = data->readFile();
					// 热写入 仅在文件内容有变化时更新写入数据
					if (!(hotWrite && std::filesystem::is_regular_file(phyPath) && readPhyFile(phyPath) == bytesArray))
					{
						std::ofstream outFile(phyPath, std::ios::binary);
						if (outFile)
						{
							// 文件数据写入
							outFile.write(bytesArray->data(), bytesArray->size());
							outFile.close();
						}
						//if (hotWrite)
						//{
						//	std::cout << "文件内容变化: " << phyPath << "\n";
						//}
					}
				}
				if (data->isDirType())
				{
					if (hotWrite)
					{
						// 当启用热写入时 若物理目录不存在该目录则无需继续向下hotWrite
						if (!std::filesystem::exists(phyPath))
						{
							hotWrite = false;
							std::filesystem::create_directory(phyPath);
						}
						createDatasToPhyPath(phyPath, data, blackExtension, hotWrite);
					}
					else
					{
						std::filesystem::create_directory(phyPath);
						createDatasToPhyPath(phyPath, data, blackExtension, false);
					}
				}
			}
		}

		static void loadPhyPathToMFS(Path* outPath, const std::filesystem::path& phyPath, const std::vector<std::string>& blackExtension = {})
		{
			if (!std::filesystem::exists(phyPath))
			{
				return;
			}
			for (const auto& entry : std::filesystem::directory_iterator(phyPath))
			{
				const auto& path = entry.path();
				const auto name = path.filename().string();
				if (entry.is_directory())
				{
					// 是文件夹目录
					loadPhyPathToMFS(&outPath->createPath(name, true), path, blackExtension);
				}
				else
				{
					if (std::find(blackExtension.begin(), blackExtension.end(), path.extension()) != blackExtension.end())
					{
						// 忽略黑名单文件类型
						continue;
					}
					// 是文件
					std::ifstream file(path, std::ios::binary | std::ios::ate);
					if (!file.is_open())
					{
						continue;
					}
					// 获取文件大小
					std::streamsize fileSize = file.tellg();
					file.seekg(0, std::ios::beg);

					std::vector<char> fileContent(fileSize);
					// 读取文件内容到vector中
					if (!file.read(fileContent.data(), fileSize))
					{
						continue;
					}
					outPath->createPath(name).writeFile(fileContent);
				}
			}
		}

		// 从物理目录加载Path
		static Path loadPhyPath(const std::filesystem::path& phyPath)
		{
			Path root{};
			if (std::filesystem::exists(phyPath))
			{
				loadPhyPathToMFS(&root, phyPath);
			}			
			return root;
		}

		MFSI::MemoryPathView getCPathView()
		{
			return getCStruct();
		}
	};
}
